#include "Avalon/Network/TcpSocketConnection.hpp"
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include "Avalon/Network/SocketException.hpp"
#include "Avalon/Network/SocketType.hpp"
#include "Avalon/Network/TcpSocketReader.hpp"
#include "Avalon/Network/TcpSocketWriter.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Network;
using namespace Avalon::Network::Detail;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace boost::posix_time;
using namespace boost::signals2;
using namespace std;

TcpSocketConnection::~TcpSocketConnection() {
  Close();
}

void TcpSocketConnection::Connect() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  if(m_isConnected) {
    return;
  }
  tcp::resolver resolver(m_socket->m_socket.get_io_service());
  tcp::resolver::query query(m_address.first,
    lexical_cast<string>(m_address.second));
  boost::system::error_code error;
  tcp::resolver::iterator end;
  tcp::resolver::iterator endpointIterator = resolver.resolve(query, error);
  if(error != 0) {
    BOOST_THROW_EXCEPTION(SocketException(error.value(),
      error.message().c_str()));
  }
  error = error::host_not_found;
  while(error != 0 && endpointIterator != end) {
    boost::system::error_code closeError;
    m_socket->m_socket.close(closeError);
    m_socket->m_socket.connect(*endpointIterator, error);
    ++endpointIterator;
  }
  if(error != 0) {
    BOOST_THROW_EXCEPTION(SocketException(error.value(),
      error.message().c_str()));
  }
  m_isConnected = true;
}

bool TcpSocketConnection::IsConnected() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  return m_isConnected;
}

void TcpSocketConnection::Close() {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  if(!m_isConnected) {
    return;
  }
  m_isConnected = false;
  m_closedSignal();
  boost::system::error_code error;
  {
    boost::shared_ptr<TcpSocketReader> reader(m_reader.lock());
    reader->Abort();
    boost::shared_ptr<TcpSocketWriter> writer(m_writer.lock());
    writer->Abort();
  }
  m_socket->m_socket.shutdown(tcp::socket::shutdown_both, error);
  if(error != 0) {
    BOOST_THROW_EXCEPTION(SocketException(error.value(),
      error.message().c_str()));
  }
  m_socket->m_socket.close(error);
  if(error != 0) {
    BOOST_THROW_EXCEPTION(SocketException(error.value(),
      error.message().c_str()));
  }
}

connection TcpSocketConnection::ConnectClosedSignal(
    const ClosedSignal::slot_type& slot) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  return m_closedSignal.connect(slot);
}

connection TcpSocketConnection::ConnectClosedSignal(
    const ClosedSignal::slot_type& slot, bool* isConnected) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  *isConnected = m_isConnected;
  return m_closedSignal.connect(slot);
}

TcpSocketConnection::TcpSocketConnection(bool isConnected,
    const IpAddress& address, const boost::shared_ptr<SocketType>& socket,
    const boost::shared_ptr<TcpSocketReader>& reader,
    const boost::shared_ptr<TcpSocketWriter>& writer)
    : m_isConnected(isConnected),
      m_address(address),
      m_socket(socket),
      m_reader(reader),
      m_writer(writer) {}
